/*
--------------------------------------------------------------------------------
				Ak-Lib. 
			 Project       	: Ak-Lib
			 Group         	: Ak-Lib R&D Team
			 Author        	: Ocean.Wang
			 Create Date 	: 05-01-2010
--------------------------------------------------------------------------------
Code Name 	:  A.K 	~v~
Description	:  Yet Another Cross-Platform Lib.
Cautions    	:
--------------------------------------------------------------------------------
Change History:
Version		: 0.1
Building 		: 05/01/10
10-05.01 first draft, build, debug and test.
--------------------------------------------------------------------------------
*/

#include "ak_timer.h"

/* Default Trace Level */
static 	int 	_gTraceLvl_STimer = TRACELVL_WARNING;

/* TIMER  Status Type */
#define STIMER_IDLE		0
#define STIMER_ACTV		1

/* -----------------------------------------------------------------------------
** Timer Service Start/Stop										
*/
int		STimer_SvrStart(STimer_Svr * pSvr)
{
	int				i;
	_t_blist 	* 	pQue;
	
	if(pSvr && (pSvr->state == _F_NO)){
		for(i=0, pQue=pSvr->scheQ; i<STIMER_ENTRY; i++, pQue++){
			BLIST_INIT(pQue);
		}
		pSvr->state = _F_YES;
		pSvr->tick 	 = 0;
		SYS_INIT_CS(&(pSvr->lock));
		return _E_SUCC;
	}
	return _E_FAIL;
}

int		STimer_SvrStop(STimer_Svr * pSvr)
{
	int 		i;
	_t_blist * 	pQue;
	if(pSvr && (pSvr->state == _F_YES)){
		pSvr->state = _F_NO;
		SYS_ENTER_CS(&(pSvr->lock));
		for(i=0, pQue=pSvr->scheQ; i<STIMER_ENTRY; i++, pQue++){
			BLIST_INIT(pQue);
		}
		SYS_LEAVE_CS(&(pSvr->lock));
		return _E_SUCC;
	}

	return _E_FAIL;
}


/* -----------------------------------------------------------------------------
** Timer Init/Start/Stop/Kill							
*/
#define STIMER_NORMALIZE(vIntv) 	(((vIntv) + STIMER_RES - 1)/STIMER_RES)

int 	STimer_Init(STimer_Node * pTimer, STimer_Svr * pSvr, _t_cbentry * pCbEntry, unsigned int intv)
{
	int ret;

	ret = _E_FAIL;
	if(pTimer && pSvr && pCbEntry && (pCbEntry->cbProc) && (intv > 0)){
		SYS_ENTER_CS(&(pSvr->lock));
		pTimer->state	= STIMER_IDLE;
		pTimer->expire 	= 0;
		pTimer->magic 	= STIMER_MAGIC;
		pTimer->pSvr 	= pSvr;
		/* Init the Parameter */
		pTimer->intv = intv;
		if(pTimer->intv < STIMER_RES){
			pTimer->intv=STIMER_RES;
		}
		pTimer->intv = STIMER_NORMALIZE(pTimer->intv);
		_CBENTRY_ASSIGN(&(pTimer->cbEntry), pCbEntry);

		SYS_TRACE(_gTraceLvl_STimer, TRACELVL_DEBUG, "Timer (%p) CB:%p - %p - %d\n",
				pTimer, 
				pCbEntry->cbProc, pCbEntry->pArg, pCbEntry->lArg);
		SYS_LEAVE_CS(&(pSvr->lock));
		ret = _E_SUCC;
	}
	return ret;
}

int		STimer_SetIntv(STimer_Node * pTimer, unsigned int intv)
{
	int 			ret;
	STimer_Svr * 	pSvr;

	ret = _E_FAIL;
	if(pTimer && ((pSvr = pTimer->pSvr) != NULL) && (intv > 0)){
		switch(pTimer->state){
			case STIMER_IDLE:
				SYS_ENTER_CS(&(pSvr->lock));
				/* Init the Parameter */
				pTimer->intv = intv;
				if(pTimer->intv < STIMER_RES){
					pTimer->intv=STIMER_RES;
				}
				pTimer->intv = STIMER_NORMALIZE(pTimer->intv);
				SYS_LEAVE_CS(&(pSvr->lock));
				ret = _E_SUCC;
				break;
				
			case STIMER_ACTV:
				SYS_TRACE(_gTraceLvl_STimer, TRACELVL_NORMAL, "Timer (%p) Active.\n", pTimer);
				break;
				
			default:
				break;
		}
	}

	return ret;
}

#define STimer_EnSche(pSvr, pTimer) \
	do{\
		(pTimer)->ring  = ((pTimer)->intv)/STIMER_ENTRY;	\
		(pTimer)->expire= ((pSvr)->tick + (pTimer)->intv); 	\
		(pTimer)->entry = STIMER_MOD((pTimer)->expire);		\
		BLST_ADD2T(&((pSvr)->scheQ[((pTimer)->entry)]),(_t_bnode *)(pTimer));\
	}while(0)

/* Here we should Just the Jump Diff :
** As Tick: 10, Intv: 500, Diff: 100
** New Expire = 610, Entry = 98, Ring = 1
** 1. 10 < Tick: <98, then Ring = 1
** 2. Tick: =88, then Ring = 1
** 3. 10 > Tick: >88, then Ring = 0
**
** As Tick: 0, Intv: 400, Diff: 100
** New Expire = 500, Entry = 500, Ring = 0
** 1. Tick: <500, then Ring = 0
** 2. Tick: =500, then Ring = 0 ?? X
** 3. Tick: >500, then Ring = 0 ?? X
*/
#define STimer_EnScheEx(pSvr, pTimer, diff, elapse) \
	do{\
		/* Normalize the Diff */ 	\
		if((diff) < STIMER_RES){\
			(diff) = STIMER_RES;\
		}\
		(diff) = STIMER_NORMALIZE(diff);\
		/* Original Tick, Timer Elapsed */ 		\
		elapse 	= (pSvr)->tick - ((pTimer)->expire - (pTimer)->intv);\
		if(elapse < (pTimer->intv)){\
			/* Calc the Ring */ 		\
			(pTimer)->intv += (diff);\
			(pTimer)->ring = (unsigned int)(((pTimer)->intv - elapse)/STIMER_ENTRY);		\
			(pTimer)->expire+= (diff);					\
		}else{\
			(pTimer)->intv += (diff);\
			(pTimer)->ring  = ((diff) /STIMER_ENTRY);	\
			(pTimer)->expire= ((pSvr)->tick + (diff));	\
		}\
		(pTimer)->entry = STIMER_MOD((pTimer)->expire);	\
		BLST_ADD2T(&((pSvr)->scheQ[(pTimer)->entry]),(_t_bnode *)(pTimer));\
	}while(0)

int		STimer_StartEx(STimer_Node * pTimer, unsigned int diff)
{
	int 				ret;
	unsigned long long 	elapse;
	STimer_Svr 	* 		pSvr;

	ret = _E_FAIL;
	if(pTimer && (pSvr = pTimer->pSvr) && (diff > 0)){
		switch(pTimer->state){
			case STIMER_IDLE:
				SYS_ENTER_CS(&(pSvr->lock));
					pTimer->state	=	STIMER_ACTV;
					if((diff) < STIMER_RES){
						(diff) = STIMER_RES;
					}
					(diff) = STIMER_NORMALIZE(diff);
					(pTimer)->intv += (diff);
					STimer_EnSche(pSvr, pTimer);
				SYS_LEAVE_CS(&(pSvr->lock));
				ret = _E_SUCC;
				break;
				
			case STIMER_ACTV:
				SYS_ENTER_CS(&(pSvr->lock));
					BLST_DEL(&(pSvr->scheQ[(pTimer->entry)]),(_t_bnode *)pTimer);
					STimer_EnScheEx(pSvr, pTimer, diff, elapse);
				SYS_LEAVE_CS(&(pSvr->lock));
				ret = _E_SUCC;
				break;

			default:
				break;
		}
	}

	return ret;
}

int		STimer_Start(STimer_Node * pTimer)
{
	int 			ret;
	STimer_Svr * 	pSvr;

	ret = _E_FAIL;
	if(pTimer && (pSvr = pTimer->pSvr) && (pTimer->intv > 0)){
		switch(pTimer->state){
			case STIMER_IDLE:
				SYS_ENTER_CS(&(pSvr->lock));
					pTimer->state	= STIMER_ACTV;
					STimer_EnSche(pSvr, pTimer);
				SYS_LEAVE_CS(&(pSvr->lock));
				ret = _E_SUCC;
				break;
				
			case STIMER_ACTV:
				SYS_ENTER_CS(&(pSvr->lock));
					BLST_DEL(&(pSvr->scheQ[(pTimer->entry)]),(_t_bnode *)pTimer);
					STimer_EnSche(pSvr, pTimer);
				SYS_LEAVE_CS(&(pSvr->lock));
				ret = _E_SUCC;
				break;

			default:
				break;
		}
	}

	return ret;
}

int		STimer_Stop (STimer_Node * pTimer)
{
	int 			ret;
	STimer_Svr * 	pSvr;

	ret = _E_FAIL;
	if(pTimer && (pSvr = pTimer->pSvr)){
		switch(pTimer->state){
			case STIMER_IDLE:
				SYS_TRACE(_gTraceLvl_STimer, TRACELVL_NORMAL, "Timer (%p) Idle.\n", pTimer);
				break;
				
			case STIMER_ACTV:
				SYS_ENTER_CS(&(pSvr->lock));
					BLST_DEL(&(pSvr->scheQ[(pTimer->entry)]), (_t_bnode *)pTimer);
					pTimer->state=STIMER_IDLE;
				SYS_LEAVE_CS(&(pSvr->lock));
				ret = _E_SUCC;
				break;
			default:
				break;
		}
	}
	
	return ret;
}

int 	STimer_Sche(STimer_Svr * pSvr, int scheCnt) 
{
	int 			i, count, expire;
	STimer_Node *	pTimer;
	STimer_Node *	pNext;
	
	_t_blist	*	pScheQ;
	_t_blist		procQ;

	if(pSvr && (pSvr->state == _F_YES)){
		while(scheCnt--){
			/* Increase Tti tick counter */
			BLIST_INIT(&procQ);			
			SYS_ENTER_CS(&(pSvr->lock));
				pSvr->tick		=(pSvr->tick+1);		
				expire 			= STIMER_MOD(pSvr->tick);

				/* Proc the Sche Que */
				pScheQ			=&(pSvr->scheQ[expire]);	
				BLST_CAT(&procQ, pScheQ);
			
				if((count = procQ.count) > 0){
					for(i=0, pTimer=(STimer_Node *)(procQ.head); i<count; i++, pTimer=pNext){
						pNext = (STimer_Node *)(pTimer->node.next);
						/* Check the Ring */
						if(pTimer->ring <= 0){	
							/* Check the Expire */
							if(STIMER_MOD(pTimer->expire - expire) != 0){
								pTimer->state=STIMER_IDLE;
								BLST_DEL(&(procQ), &(pTimer->node));
								SYS_TRACE(_gTraceLvl_STimer, TRACELVL_WARNING, "Timer (%p) Expire: %d - (%d)\n", 
									pTimer, pTimer->expire, pTimer->intv);
								continue;
							}
						}else if(pTimer->ring > 0){
							pTimer->ring	-= 1;
							BLST_DEL(&(procQ), &(pTimer->node));
							BLST_ADD2T(pScheQ, &(pTimer->node));
							continue;
						}
					}
				}
			SYS_LEAVE_CS(&(pSvr->lock));

			/* Now Let's Process the Real CbEntry */
			while((pTimer=(STimer_Node *)BLST_GET(&procQ))){
				pTimer->state	=STIMER_IDLE;
				pTimer->expire	=0;
				pTimer->entry 	=0;
				if(pTimer->cbEntry.cbProc){
					pTimer->cbEntry.cbProc(pTimer->cbEntry.pArg, pTimer->cbEntry.lArg);
				}
			}
		}
	}
	
	return _E_SUCC;
}



